package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
	"reflect"
	"time"

	"gitee.com/cjianwen/go/bincode"
	"gitee.com/cjianwen/go/demo/bincode/model"
	"gitee.com/cjianwen/go/iohelper"
	"gitee.com/cjianwen/go/socks/v2"
	"google.golang.org/protobuf/proto"
)

type AAA struct {
	A uint8
	B int8
}

type Test1 struct {
	A  byte
	B  int16
	C  int32
	D  int64
	E  string
	SS []string
	Bs []byte
	// AS  []AAA
	AA AAA
	// Arr [2]AAA
}

type Test2 struct {
	DocId       uint32 `protobuf:"varint,1,opt,name=doc_id,json=docId,proto3" json:"doc_id,omitempty"`
	Position    string `protobuf:"bytes,2,opt,name=position,proto3" json:"position,omitempty"`
	Company     string `protobuf:"bytes,3,opt,name=company,proto3" json:"company,omitempty"`
	City        string `protobuf:"bytes,4,opt,name=city,proto3" json:"city,omitempty"`
	SchoolLevel int32  `protobuf:"varint,5,opt,name=school_level,json=schoolLevel,proto3" json:"school_level,omitempty"`
	Vip         byte   `protobuf:"varint,6,opt,name=vip,proto3" json:"vip,omitempty"`
	Chat        byte   `protobuf:"varint,7,opt,name=chat,proto3" json:"chat,omitempty"`
	Active      int32  `protobuf:"varint,8,opt,name=active,proto3" json:"active,omitempty"`
	WorkAge     int32  `protobuf:"varint,9,opt,name=work_age,json=workAge,proto3" json:"work_age,omitempty"`
}

type Test = Test2

type typeHash map[reflect.Type]int

const loopCount = 1000000

const maxint = 0x7FFFFFFF

var global_test = Test{DocId: maxint, Position: "pos", Company: "company", City: "city", SchoolLevel: maxint, Vip: 1, Chat: 1, Active: maxint, WorkAge: maxint} //, Active: 128*128 + 1, WorkAge: 128 * 128 * 128
var s = bincode.NewSerializer(false, 1023)

func Init() {
	s.RegisterType(1, &Test{})
}

func TestOld() {

	s := socks.NewSerializer(false, 1023)
	s.RegisterType(1, &Test{})
	test1 := &global_test

	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, []AAA{{1, 2}}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}
	test := &Test{}

	tp1 := reflect.TypeOf(test1)
	tp2 := reflect.ValueOf(test)

	if tp1.Kind() == reflect.Ptr {
		tp1 = tp1.Elem()
	}

	if tp2.Type().Kind() == reflect.Ptr {
		tp2 = reflect.Indirect(tp2)
	}

	mp := make(typeHash)
	mp[tp1] = 1

	if tp1 == tp2.Type() {
		fmt.Println("==", tp1, mp[tp2.Type()])
	} else {
		fmt.Println("not same")
	}

	var buf bytes.Buffer
	now := time.Now()

	enc := gob.NewEncoder(&buf)
	dec := gob.NewDecoder(&buf)
	// 预热，避免将初始化时间计算在内
	for i := 0; i < loopCount; i++ {
		err := enc.Encode(test)
		if err != nil {
			log.Panic("encode error:", err)
		} else {
			// fmt.Println("gob bytes = len = ", len(buf.Bytes()))
		}
		err = dec.Decode(test1)
		if err != nil {
			log.Panic("decode error:", err)
		}
	}
	fmt.Println("gob runtime = ", time.Since(now).Milliseconds())

	now = time.Now()
	for i := 0; i < loopCount; i++ {
		bs, ok := s.Encode(test)
		if !ok {
			fmt.Println("encode error: ")
		}

		ok = s.Decode(bs, test1)
		if !ok {
			fmt.Println("decode error: ")
		}
	}

	fmt.Println("old runtime = ", time.Since(now).Milliseconds())
}

func TestNow() {
	test1 := &global_test

	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, []AAA{{1, 2}}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}
	test := &Test{}

	var buf bytes.Buffer
	now := time.Now()

	enc := gob.NewEncoder(&buf)
	dec := gob.NewDecoder(&buf)
	// 预热，避免将初始化时间计算在内
	for i := 0; i < loopCount; i++ {
		err := enc.Encode(test)
		if err != nil {
			log.Panic("encode error:", err)
		} else {
			// fmt.Println("gob bytes = len = ", len(buf.Bytes()))
		}
		err = dec.Decode(test1)
		if err != nil {
			log.Panic("decode error:", err)
		}
	}
	fmt.Println("gob runtime = ", time.Since(now).Milliseconds())

	now = time.Now()
	var buff []byte
	var err error
	for i := 0; i < loopCount; i++ {
		buff, err = s.Encode(test)
		if err != nil {
			fmt.Println("encode error: ", err)
		}

		n2, e2 := s.Decode(buff, test1)
		if n2 != len(buff) || e2 != nil {
			fmt.Println("decode error: ", err, n2, len(buff))
		}
	}

	fmt.Println("new serialize runtime = ", time.Since(now).Milliseconds(), ", len = ", len(buff))
}

func TestProto() {
	// const maxint = 0x7FFFFFFF
	// doc := &model.Doc{DocId: maxint, Position: "pos", Company: "company", City: "city", SchoolLevel: maxint, Vip: true, Chat: true, Active: maxint, WorkAge: maxint} //, Active: 128*128 + 1, WorkAge: 128 * 128 * 128
	doc := &model.Doc{DocId: 1, Chat: true, Active: 123, WorkAge: 33, WorkAge23: 66} //, Active: 128*128 + 1, WorkAge: 128 * 128 * 128
	var doc2 model.Doc
	now := time.Now()
	for i := 0; i < loopCount; i++ {
		bits, err := proto.Marshal(doc)
		if err != nil {
			fmt.Println("proto buff bits len ", len(bits), err, bits, doc.DocId, doc)
		}
		proto.Unmarshal(bits, &doc2)
	}
	fmt.Println("proto buff use time: ", time.Since(now).Milliseconds())
	bits, _ := proto.Marshal(doc)
	fmt.Println("proto buff: bits = ", bits, ", len = ", len(bits))
}

func main() {

	fmt.Println(iohelper.ByteOrd.String())

	// TestOld()
	TestProto()
	// TestNow()

	return
	s.RegisterType(1, &Test{})

	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, []AAA{{1, 2}}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}
	// test := &Test{1, 2, 3, 4, "123", []string{"aaa", "bbb"}, []byte{1, 2, 3, 4}, AAA{11, 22}, [2]AAA{{1, 2}, {3, 4}}}

	// test := []Test{{DocId: 1}, {DocId: 2, Position: "test pos", Company: "microsoft", WorkAge: 15}}
	// var test1 []Test

	test1 := global_test
	var test Test

	tp1 := reflect.TypeOf(test1)
	tp2 := reflect.ValueOf(test)

	if tp1.Kind() == reflect.Ptr {
		tp1 = tp1.Elem()
	}

	if tp2.Type().Kind() == reflect.Ptr {
		tp2 = reflect.Indirect(tp2)
	}

	mp := make(typeHash)
	mp[tp1] = 1

	if tp1 == tp2.Type() {
		fmt.Println("==", tp1, mp[tp2.Type()])
	} else {
		fmt.Println("not same")
	}

	// return
	bits, err := s.Encode(&test)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("encode ok")
	}
	fmt.Println("len = ", len(bits), bits)

	if _, err = s.Decode(bits, &test1); err == nil {
		fmt.Println("decode ok")
	} else {
		fmt.Println("decode failed. ", err)
	}

	fmt.Printf("%#+v \n%#+v", test, test1)
}
